﻿using System;
using System.Reflection;

namespace MadMediaManager.WPFSupport.Services.Helpers
{
    /// <summary>
    /// This class is an implementation detail of the MessageToActionsMap class.
    /// </summary>
    internal class WeakAction : IEquatable<WeakAction>
    {
        #region Constructor

        /// <summary>
        /// Constructs a WeakAction.
        /// </summary>
        /// <param name="target">The object on which the target method is invoked, or null if the method is static.</param>
        /// <param name="method">The MethodInfo used to create the Action.</param>
        /// <param name="parameterType">The type of parameter to be passed to the action. Pass null if there is no parameter.</param>
        internal WeakAction(object target, MethodInfo method, Type parameterType)
        {
            _targetRef = target == null ? null : new WeakReference(target);

            _method = method;

            ParameterType = parameterType;

            _delegateType = parameterType == null ? typeof(Action) : typeof(Action<>).MakeGenericType(parameterType);
        }

        #endregion Constructor

        #region CreateAction

        /// <summary>
        /// Creates a "throw away" delegate to invoke the method on the target, or null if the target object is dead.
        /// </summary>
        internal Delegate CreateAction()
        {
            if (_targetRef == null)
            {
                return Delegate.CreateDelegate(_delegateType, _method);
            }

            var target = _targetRef.Target;

            return target != null ? Delegate.CreateDelegate(_delegateType, target, _method) : null;
        }

        #endregion CreateAction

        #region Fields

        internal readonly Type ParameterType;

        readonly Type _delegateType;
        readonly MethodInfo _method;
        readonly WeakReference _targetRef;

        #endregion // Fields

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            return obj.GetType() == typeof(WeakAction) && Equals(obj as WeakAction);
        }

        public bool Equals(WeakAction other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.ParameterType, ParameterType) &&
                   Equals(other._delegateType, _delegateType) &&
                   Equals(other._method, _method) &&
                   (_targetRef == null || other._targetRef == null ? Equals(other._targetRef, _targetRef) : Equals(other._targetRef.Target, _targetRef.Target));
        }

        public override int GetHashCode()
        {
            unchecked
            {
                int result = (ParameterType != null ? ParameterType.GetHashCode() : 0);
                result = (result*397) ^ (_delegateType != null ? _delegateType.GetHashCode() : 0);
                result = (result*397) ^ (_method != null ? _method.GetHashCode() : 0);
                result = (result*397) ^ (_targetRef != null ? _targetRef.GetHashCode() : 0);
                return result;
            }
        }
    }
}
